import "reflect-metadata";
import {ReflectMeta, RequestMethod, Params} from "../utils";

export interface ValidateObjectKeys {
  name: string,
  required: boolean,
  value?: string
}


export const Controller = (path: string): ClassDecorator => {
  return (target: Function) => {
    Reflect.defineMetadata(ReflectMeta.PATH_META, path ?? "/api", target);
  };
};

const createMethod = (methods: string) => {
  return (path: string): MethodDecorator => {
    return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
      Reflect.defineMetadata(ReflectMeta.PATH_META, path, target, propertyKey);
      Reflect.defineMetadata(ReflectMeta.METHOD_META, methods, target, propertyKey);
      Reflect.defineMetadata(ReflectMeta.DESCRIPTOR_META, descriptor, target, propertyKey);
    };
  };
};

export const Get = createMethod(RequestMethod.GET);
export const Post = createMethod(RequestMethod.POST);
export const Delete = createMethod(RequestMethod.DELETE);
export const Put = createMethod(RequestMethod.PUT);


/**
 * param: body | query
 *
 */
const createParams = (param: string): Function => {
  return (keys?: ValidateObjectKeys[]): ParameterDecorator => {
    return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
      const functionStr = target[propertyKey as keyof typeof target].toString();
      const startIndex = functionStr.indexOf("(");
      const endIndex = functionStr.indexOf(")");
      const argumentsArr = functionStr.substring(startIndex + 1, endIndex).split(",");
      let attr = {
        attrName: argumentsArr[parameterIndex].trim(),
        subAttr: keys
      };
      let params = Reflect.getMetadata(ReflectMeta.PARAMS_META, target, propertyKey) || [];
      params.push({target: param, attr: attr, index: parameterIndex});
      Reflect.defineMetadata(ReflectMeta.PARAMS_META, params, target, propertyKey);
    };
  };
};
export const Body = createParams(Params.Body);
export const Query = createParams(Params.Query);

const createRecordDecorator = (): Function => {
  return (recordModel: string): MethodDecorator => {
    return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
      Reflect.defineMetadata(ReflectMeta.RECORD_META, {recordModel}, target, propertyKey);
    };
  };
};

export const Record = createRecordDecorator();